﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using Microsoft.Internal;
using Microsoft.Internal.Collections;

namespace System.ComponentModel.Composition
{
    // Provides helpers for creating and dealing with Exports
    internal static partial class ExportServices
    {
        internal static readonly Type DefaultMetadataViewType = typeof(IDictionary<string, object>);
        internal static readonly Type DefaultExportedObjectType = typeof(object);

        internal static Export CreateStronglyTypedExportFromExport(Type exportedObjectType, Type metadataViewType, Export export)
        {
            Assumes.NotNull(exportedObjectType, export);

            Type specializedExportType = MakeGenericExportType(exportedObjectType, metadataViewType);

            return CreateExport(specializedExportType, export);
        }

        internal static IEnumerable CreateStronglyTypedExportCollectionFromExports(SpecializedExportType exportType, IEnumerable<Export> exports)
        {
            Assumes.NotNull(exportType, exports);

            // Create either an ExportCollection, ExportCollection<T> or ExportCollection<T, TMetadataView>, 
            // and populate it with Export<[exportedObjectType], [metadataViewType] instances

            IList exportCollection = CreateExportCollection(exportType.CollectionExportType);

            foreach (Export export in exports)
            {
                Export wrappedExport = CreateExport(exportType.ElementExportType, export);
                exportCollection.Add(wrappedExport);
            }

            return exportCollection;
        }

        internal static bool IsKnownExportType(Type type)
        {
            Assumes.NotNull(type);

            if (type.IsGenericType)
            {
                Type genericType = type.GetGenericTypeDefinition();
                return (genericType == typeof(Export<>) || genericType == typeof(Export<,>));
            }

            return (type == typeof(Export));
        }

        internal static bool IsDefaultMetadataViewType(Type metadataViewType)
        {
            Assumes.NotNull(metadataViewType);

            // Consider all types that IDictionary<string, object> derives from, such
            // as ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>> 
            // and IEnumerable, as default metadata view
            return metadataViewType.IsAssignableFrom(DefaultMetadataViewType);
        }

        internal static bool IsDictionaryConstructorViewType(Type metadataViewType)
        {
            Assumes.NotNull(metadataViewType);

            // Does the view type have a constructor that is a Dictionary<string, object>
            return metadataViewType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                                    Type.DefaultBinder,
                                                    new Type[] { typeof(IDictionary<string, object>) },
                                                    new ParameterModifier[0]) != null;
        }

        internal static Export CreateExport(Type exportType, Export export)
        {
            Assumes.NotNull(exportType, export);

            return (Export)Activator.CreateInstance(exportType, export);
        }

        private static IList CreateExportCollection(Type collectionType)
        {
            Assumes.NotNull(collectionType);

            return (IList)Activator.CreateInstance(collectionType);
        }

        internal static Type MakeGenericExportType(Type exportedObjectType, Type metadataViewType)
        {
            if (exportedObjectType == null)
            {
                exportedObjectType = DefaultExportedObjectType;
            }

            if (metadataViewType == null)
            {
                metadataViewType = DefaultMetadataViewType;
            }

            // Returns StructuredValueExport<[exportedObjectType], [metadataViewType]>
            return typeof(StructuredValueExport<,>).MakeGenericType(exportedObjectType, metadataViewType);
        }

        internal static Type MakeGenericExportCollectionType(Type exportedObjectType, Type metadataViewType)
        {
            if (exportedObjectType == null)
            {
                return typeof(ExportCollection);
            }

            if (metadataViewType == null)
            {
                // Returns ExportCollection<[exportedObjectType]>
                return typeof(ExportCollection<>).MakeGenericType(exportedObjectType);
            }

            // Returns ExportCollection<[exportedObjectType], [metadataViewType]>
            return typeof(ExportCollection<,>).MakeGenericType(exportedObjectType, metadataViewType);
        }

        internal static ExportCardinalityCheckResult CheckCardinality(ImportDefinition definition, IEnumerable<Export> exports)
        {
            EnumerableCardinality actualCardinality = exports.GetCardinality();

            switch (actualCardinality)
            {
                case EnumerableCardinality.Zero:
                    if (definition.Cardinality == ImportCardinality.ExactlyOne)
                    {
                        return ExportCardinalityCheckResult.NoExports;
                    }
                    break;

                case EnumerableCardinality.TwoOrMore:
                    if (definition.Cardinality.IsAtMostOne())
                    {
                        return ExportCardinalityCheckResult.TooManyExports;
                    }
                    break;

                default:
                    Assumes.IsTrue(actualCardinality == EnumerableCardinality.One);
                    break;

            }

            return ExportCardinalityCheckResult.Match;
        }
    }
}
